package com.hebeiwangxiaozhang.user.service.service;

import com.alibaba.fastjson.JSONObject;
import com.hebeiwangxiaozhang.response.CommonResponse;
import com.hebeiwangxiaozhang.response.ResponseCode;
import com.hebeiwangxiaozhang.response.ResponseUtils;
import com.hebeiwangxiaozhang.user.service.config.GiteeConfig;
import com.hebeiwangxiaozhang.user.service.pojo.*;
import com.hebeiwangxiaozhang.user.service.repo.OauthClientRegisterRepository;
import com.hebeiwangxiaozhang.user.service.repo.OauthClientRepository;
import com.hebeiwangxiaozhang.user.service.repo.UserRepository;
import com.hebeiwangxiaozhang.user.service.utils.RedisCommonProcessor;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/*
1. 补充user信息，save的时候 set全面，检查
2. 全局唯一Id 生成，新增user id
3. User 登录成功后，将user信息存入 redis，前端携带 userid 在header中，后续可以通过userid 做限流
 */

//no need oauth
@Service
public class UserRegisterService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private OauthClientRepository oauthClientRepository;

    @Autowired
    private RedisCommonProcessor redisCommonProcessor;

    @Autowired
    private GiteeConfig giteeConfig;

    @Autowired
    private RestTemplate outerRestTemplate;

    @Autowired
    private RestTemplate innerRestTemplate;

    @Autowired
    private OauthClientRegisterRepository oauthClientRegisterRepository;

    //如果当前存在事务，则加入该事务，如果当前不存在事务，则创建一个新的事务。
//    @Transactional(propagation = Propagation.REQUIRED)
    public CommonResponse namePasswordRegister(User user) {
        if(userRepository.findByUserName(user.getUserName()) == null
                && oauthClientRepository.findByClientId(user.getUserName()) == null) {
            BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
            String password = user.getPasswd();
            String encodePassword = bcryptPasswordEncoder.encode(password);
            user.setPasswd(encodePassword);
            OauthClient oauthClient = OauthClient.builder()
                    .clientId(user.getUserName())
                    .resourceIds(RegisterType.USER_PASSWORD.name())
                    .clientSecret(encodePassword)
                    .scope("web")
                    .authorizedGrantTypes(AuthGrantType.refresh_token.name()
                            .concat(",")
                            .concat(AuthGrantType.password.name()))
                    .authorities(RegisterType.USER_PASSWORD.name())
                    .build();
            Integer userId = this.saveUserAndOauthClient(user, oauthClient);
            String singlePersonId = String.valueOf(userId +10000000);
            redisCommonProcessor.set(singlePersonId, user, 1296000);
            return ResponseUtils.okResponse(formatResponseContent(user,
                    generateOauthToken(AuthGrantType.password,
                            user.getUserName(),
                            password,
                            user.getUserName(),
                            password)));
        }
        return ResponseUtils.failResponse(ResponseCode.BAD_REQUEST.getCode(), null, "User Account Already exist, please login!");
    }

    public CommonResponse login(String username, String password) {
        User user = userRepository.findByUserName(username);
        if(user == null) {
            return ResponseUtils.failResponse(ResponseCode.BAD_REQUEST.getCode(), null, "User not found!");
        }
        Map content = formatResponseContent(user,
                generateOauthToken(AuthGrantType.password,
                        username,
                        password,
                        username,
                        password));
        String singlePersonId = String.valueOf(user.getId() +10000000);
        redisCommonProcessor.set(singlePersonId, user, 1296000);
        return ResponseUtils.okResponse(content);

    }

//    @Transactional(propagation = Propagation.REQUIRED)
    public CommonResponse phoneCodeRegister(String phoneNumber, String code) {
        String cacheCode = String.valueOf(redisCommonProcessor.get(phoneNumber));
        if(StringUtils.isEmpty(cacheCode)) {
            return ResponseUtils.failResponse(ResponseCode.BAD_REQUEST.getCode(), null,
                    "Phone verify-code is expired!");
        }
        if(!cacheCode.equals(code)) {
            return ResponseUtils.failResponse(ResponseCode.BAD_REQUEST.getCode(), null,
                    "Phone verify-code is incorrect!");
        }
        BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
        String encodePassword = bcryptPasswordEncoder.encode(code);
        User user = userRepository.findByUserPhone(phoneNumber);
        if(user == null) {
            String userName = getSystemDefinedUserName(phoneNumber);
            user = User.builder()
                    .userName(userName)
                    .passwd("")
                    .userPhone(phoneNumber)
                    .userRole(RegisterType.PHONE_NUMBER.name())
                    .vipStatus(0)
                    .vipEpoch(0).build();
            OauthClient oauthClient = OauthClient.builder()
                    .clientId(phoneNumber)
                    .resourceIds(RegisterType.PHONE_NUMBER.name())
                    .clientSecret(encodePassword)
                    .scope("web")
                    .authorizedGrantTypes(AuthGrantType.refresh_token.name()
                            .concat(",")
                            .concat(AuthGrantType.client_credentials.name()))
                    .authorities(RegisterType.PHONE_NUMBER.name())
                    .build();
            Integer userId = this.saveUserAndOauthClient(user, oauthClient);
            String singlePersonId = String.valueOf(userId +10000000);
            redisCommonProcessor.set(singlePersonId, user, 1296000);
        } else {
            oauthClientRepository.updateSecretByPhoneNumber(encodePassword, phoneNumber);
        }
        return ResponseUtils.okResponse(formatResponseContent(user,
                generateOauthToken(AuthGrantType.client_credentials,
                        null,
                        null,
                        phoneNumber,
                        code)));
    }

    //手机账号注册用户，生成自定义的 用户id
    //统一前缀 + 时间戳+手机尾号后四位
    private String getSystemDefinedUserName(String phoneNumber) {
        return "MALL_" + System.currentTimeMillis() + phoneNumber.substring(phoneNumber.length() - 4);
    }

    public CommonResponse thirdPartSystem(HttpServletRequest request, String thirdPartyType) {
        String code = request.getParameter("code");
        String state = request.getParameter("state");
        if(!giteeConfig.getState().equals(state)) {
            throw new UnsupportedOperationException("Invalid state!");
        }

        //请求Gitee平台获取token，并携带code
        String tokenUrl = String.format(giteeConfig.getTokenUrl(),
                giteeConfig.getClientId(),
                giteeConfig.getClientSecret(),
                giteeConfig.getCallBack(),
                code);
        JSONObject tokenResponse = outerRestTemplate.postForObject(tokenUrl, null, JSONObject.class);
        String token = String.valueOf(tokenResponse.get("access_token"));
        System.out.println(token);
        //请求用户信息，并携带 token
        String userUrl = String.format(giteeConfig.getUserUrl(), token);
        JSONObject userInfoResponse = outerRestTemplate.getForObject(userUrl, JSONObject.class);

        //获取用户信息，userName添加前缀 GITEE@, 密码保持与userName一致。讨论过程请参见2.3小节
        String userName = giteeConfig.getState().concat(String.valueOf(userInfoResponse.get("name")));
        String password = userName;
        BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
        String encodePassword = bcryptPasswordEncoder.encode(password);
        User user = userRepository.findByUserName(userName);
        if(user == null) {
            user = User.builder()
                    .userName(userName)
                    .passwd(encodePassword)
                    .userRole(RegisterType.THIRD_PARTY.name())
                    .vipStatus(0)
                    .vipEpoch(0).build();
            OauthClient oauthClient = OauthClient.builder()
                    .clientId(userName)
                    .resourceIds(thirdPartyType)
                    .clientSecret(encodePassword)
                    .scope("web")
                    .authorizedGrantTypes(AuthGrantType.refresh_token.name()
                            .concat(",")
                            .concat(AuthGrantType.client_credentials.name()))
                    .authorities(RegisterType.THIRD_PARTY.name())
                    .build();
            this.saveUserAndOauthClient(user, oauthClient);
        }
        return ResponseUtils.okResponse(formatResponseContent(user,
                generateOauthToken(AuthGrantType.client_credentials,
                        null,
                        null,
                        userName,
                        password)));
    }

    @Resource(name = "transactionManager")
    private JpaTransactionManager transactionManager;
    private Integer saveUserAndOauthClient(User user, OauthClient oauthClient) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        def.setTimeout(30);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            user = this.userRepository.save(user);
            this.oauthClientRepository.save(oauthClient);
            transactionManager.commit(status);
        } catch (Exception e) {
            if(!status.isCompleted()){
                transactionManager.rollback(status);
            }
            throw new UnsupportedOperationException("DB Save failed!!");
        }
        return user.getId();
    }

    private Map generateOauthToken(AuthGrantType grantType, String userName, String password, String clientId, String clientSecret) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", grantType.name());
        params.add("client_id", clientId);
        params.add("client_secret", clientSecret);
        if(grantType == AuthGrantType.password) {
            params.add("username", userName);
            params.add("password", password);
        }
        HttpEntity<MultiValueMap<String, String>> requestEntity
                = new HttpEntity<MultiValueMap<String, String>>(params, headers);
        return innerRestTemplate.postForObject
                ("http://oauth2-service/oauth/token", requestEntity, Map.class);
    }

    private Map formatResponseContent(User user, Map oauthClient) {
        Map<String, Object> content = new HashMap<>();
        content.put("user", user);
        content.put("oauth", oauthClient);
        return content;
    }

    public CommonResponse thirdParAppRequest(String personId, Ouath2ClientRegister ouath2ClientRegister) {
        Integer userId = Integer.valueOf(personId) - 10000000;
        ouath2ClientRegister.setClientId(UUID.randomUUID().toString().replaceAll("-",""));
        ouath2ClientRegister.setClientSecret(UUID.randomUUID().toString().replaceAll("-",""));
        ouath2ClientRegister.setApprove(0);
        ouath2ClientRegister.setUserId(userId);
        oauthClientRegisterRepository.save(ouath2ClientRegister);
        return ResponseUtils.okResponse(null);
    }

    public CommonResponse checkThirdParAppRequestStatus(String personId) {
        Integer userId = Integer.valueOf(personId) - 10000000;
        List<Ouath2ClientRegister> userRequestInfo = oauthClientRegisterRepository.findByUserId(userId);
        return ResponseUtils.okResponse(userRequestInfo);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public CommonResponse checkThirdParAppRequestApprove(String appName) {
        Ouath2ClientRegister ouath2ClientRegister = this.oauthClientRegisterRepository.findByAppName(appName);
        this.oauthClientRegisterRepository.updateRegisterClientByAppName(appName);
        OauthClient oauth2Client = OauthClient.builder()
                .clientId(ouath2ClientRegister.getClientId())
                .resourceIds(appName)
                .clientSecret(new BCryptPasswordEncoder().encode(ouath2ClientRegister.getClientSecret()))
                .scope("web")
                .redirectUrl(ouath2ClientRegister.getAppCallbackUrl())
                .authorities(appName)
                .autoApprove("true")
                .authorizedGrantTypes(AuthGrantType.refresh_token.name().concat(",").concat(AuthGrantType.authorization_code.name()))
                .build();
        this.oauthClientRepository.save(oauth2Client);
        return ResponseUtils.okResponse(null);
    }
}
